import { useCallback, useEffect, useRef, useState } from "react";
import AwsS3, { type AwsS3UploadParameters } from "@uppy/aws-s3";
import Uppy, { type UppyFile } from "@uppy/core";
import { useTranslations } from "next-intl";
import { toast } from "sonner";

import { UPLOAD_CONFIG } from "@/config/upload-config";
import {
  abortMultipartUpload,
  completeMultipartUpload,
  createMultipartUpload,
  getMultipartPartUrl,
} from "@/http/endpoints/files";

/**
 * Options for the useUppyUpload hook
 */
export interface UseUppyUploadOptions {
  /**
   * Validation before adding file to Uppy (checkFile endpoint)
   */
  onValidate?: (file: File) => Promise<void>;

  /**
   * Generate objectName and perform pre-upload logic
   */
  onBeforeUpload?: (file: File) => Promise<string>;

  /**
   * Register file in backend after successful upload
   */
  onAfterUpload?: (fileId: string, file: File, objectName: string) => Promise<void>;

  /**
   * Function to get presigned URL for S3 upload
   * Returns the URL, method, and the ACTUAL objectName generated by backend
   */
  getPresignedUrl: (
    objectName: string,
    extension: string
  ) => Promise<{ url: string; method: string; actualObjectName?: string }>;

  /**
   * Callback when all uploads complete successfully
   */
  onSuccess?: () => void;

  /**
   * Optional folder context for file organization
   */
  currentFolderId?: string;
}

/**
 * Status of a file upload
 */
export type FileUploadStatus = "pending" | "uploading" | "success" | "error" | "cancelled";

/**
 * State of a file upload
 */
export interface FileUploadState {
  id: string;
  file: File;
  status: FileUploadStatus;
  progress: number;
  error?: string;
  objectName?: string;
  previewUrl?: string;
}

/**
 * Custom hook for managing file uploads with Uppy
 * Provides a simple interface for components to handle file uploads
 */
export function useUppyUpload(options: UseUppyUploadOptions) {
  const t = useTranslations();
  const uppyRef = useRef<Uppy | null>(null);
  const [fileUploads, setFileUploads] = useState<FileUploadState[]>([]);
  const [isUploading, setIsUploading] = useState(false);

  // Store callbacks in refs to avoid recreating Uppy instance
  const onValidateRef = useRef(options.onValidate);
  const onBeforeUploadRef = useRef(options.onBeforeUpload);
  const onAfterUploadRef = useRef(options.onAfterUpload);
  const getPresignedUrlRef = useRef(options.getPresignedUrl);
  const onSuccessRef = useRef(options.onSuccess);

  // Update refs when callbacks change
  useEffect(() => {
    onValidateRef.current = options.onValidate;
    onBeforeUploadRef.current = options.onBeforeUpload;
    onAfterUploadRef.current = options.onAfterUpload;
    getPresignedUrlRef.current = options.getPresignedUrl;
    onSuccessRef.current = options.onSuccess;
  }, [options]);

  // Initialize Uppy instance only once
  useEffect(() => {
    const uppy = new Uppy({
      autoProceed: false, // Manual control via startUpload()
      allowMultipleUploads: true,
      restrictions: {
        // No global restrictions - handled per context
      },
    });

    // Setup AWS S3 plugin with conditional multipart support
    // Files <100MB: Use simple PUT upload
    // Files ≥100MB: Use multipart chunked upload
    uppy.use(AwsS3, {
      limit: 6, // Allow 6 concurrent part uploads
      getChunkSize: () => 8 * 1024 * 1024, // 8MB chunk size
      shouldUseMultipart: (file: any) => {
        const fileSize = file.size || 0;
        const useMultipart = fileSize >= UPLOAD_CONFIG.MULTIPART_THRESHOLD;
        return useMultipart;
      },

      // For simple uploads (<100MB)
      async getUploadParameters(file: UppyFile<any, any>): Promise<AwsS3UploadParameters> {
        try {
          // 1. Validate file if validation callback is provided
          if (onValidateRef.current) {
            try {
              await onValidateRef.current(file.data as File);
            } catch (error: any) {
              const errorMessage = error.message || "Validation failed";
              setFileUploads((prev) =>
                prev.map((f) => (f.id === file.id ? { ...f, status: "error", error: errorMessage } : f))
              );
              throw error;
            }
          }

          // 2. Generate object name
          let objectName: string;
          if (onBeforeUploadRef.current) {
            objectName = await onBeforeUploadRef.current(file.data as File);
          } else {
            objectName = file.name || "untitled";
          }

          // 3. Get presigned URL
          const extension = (file.name || "").split(".").pop() || "";
          const result = await getPresignedUrlRef.current(objectName, extension);

          // Use actualObjectName from backend if provided
          const finalObjectName = result.actualObjectName || objectName;

          // Store the FINAL object name in file metadata
          uppy.setFileMeta(file.id, { objectName: finalObjectName });

          return {
            method: "PUT" as const,
            url: result.url,
            headers: {
              "Content-Type": file.type || "application/octet-stream",
            },
          };
        } catch (error) {
          console.error("[Upload] Failed to get upload parameters:", error);
          throw error;
        }
      },

      // For multipart uploads (≥100MB)
      async createMultipartUpload(file: UppyFile<any, any>) {
        try {
          // 1. Validate file
          if (onValidateRef.current) {
            await onValidateRef.current(file.data as File);
          }

          // 2. Generate object name
          let objectName: string;
          if (onBeforeUploadRef.current) {
            objectName = await onBeforeUploadRef.current(file.data as File);
          } else {
            objectName = file.name || "untitled";
          }

          const extension = (file.name || "").split(".").pop() || "";
          const filename = objectName.replace(`.${extension}`, "");

          // 3. Create multipart upload on backend
          const response = await createMultipartUpload({
            filename,
            extension,
          });

          const { uploadId, objectName: actualObjectName } = response.data;

          // Store metadata
          uppy.setFileMeta(file.id, {
            objectName: actualObjectName,
            uploadId,
          });

          return {
            uploadId,
            key: actualObjectName,
          };
        } catch (error) {
          console.error("[Upload:Multipart] Failed to create multipart upload:", error);
          throw error;
        }
      },

      //TODO: List parts (for resuming multipart uploads)
      async listParts(file: UppyFile<any, any>, { uploadId, key }: any) {
        console.log(`[Upload:Multipart] Listing parts for: ${file.name}`);
        console.log(`Upload ID: ${uploadId}, Key: ${key}`);
        // Para simplificar, não vamos implementar resumo de upload por enquanto
        // Retornamos array vazio indicando que não há partes já enviadas
        return [];
      },

      // Sign individual parts for multipart upload
      async signPart(file: UppyFile<any, any>, partData: any) {
        const { uploadId, key, partNumber } = partData;

        try {
          const response = await getMultipartPartUrl({
            uploadId,
            objectName: key,
            partNumber: partNumber.toString(),
          });

          // Return the signed URL object directly - Uppy expects { url, headers }
          return {
            url: response.data.url,
            headers: {},
          };
        } catch (error) {
          console.error(`[Upload:Multipart] Failed to sign part ${partNumber}:`, error);
          throw error;
        }
      },

      // Complete multipart upload
      async completeMultipartUpload(file: UppyFile<any, any>, data: any) {
        const { uploadId, key, parts } = data;
        const meta = file.meta as { objectName: string };

        try {
          await completeMultipartUpload({
            uploadId,
            objectName: meta.objectName || key,
            parts,
          });

          return {};
        } catch (error) {
          console.error("[Upload:Multipart] Failed to complete multipart upload:", error);
          throw error;
        }
      },

      async abortMultipartUpload(file: UppyFile<any, any>, data: any) {
        const { uploadId, key } = data;
        const meta = file.meta as { objectName: string };

        try {
          await abortMultipartUpload({
            uploadId,
            objectName: meta.objectName || key,
          });
        } catch (error) {
          console.error("[Upload:Multipart] Failed to abort multipart upload:", error);
          // Don't throw - abort is cleanup, shouldn't fail the operation
        }
      },
    });

    uppyRef.current = uppy;

    // Cleanup on unmount only
    return () => {
      const files = uppy.getFiles();
      files.forEach((file) => {
        uppy.removeFile(file.id);
      });
    };
  }, []); // Empty dependency array - only run once

  // Event listeners for Uppy
  useEffect(() => {
    const uppy = uppyRef.current;
    if (!uppy) return;

    // When file is added to Uppy
    const handleFileAdded = (file: any) => {
      setFileUploads((prev) => [
        ...prev,
        {
          id: file.id,
          file: file.data,
          status: "pending",
          progress: 0,
          previewUrl: file.data.type.startsWith("image/") ? URL.createObjectURL(file.data) : undefined,
        },
      ]);
    };

    // Upload progress updates
    const handleProgress = (file: any, progress: any) => {
      const percent = (progress.bytesUploaded / progress.bytesTotal) * 100;
      setFileUploads((prev) =>
        prev.map((f) => (f.id === file.id ? { ...f, status: "uploading", progress: Math.round(percent) } : f))
      );
    };

    // Upload success
    const handleSuccess = async (file: any) => {
      const objectName = file.meta.objectName;

      try {
        // Call registration callback
        if (onAfterUploadRef.current) {
          await onAfterUploadRef.current(file.id, file.data, objectName);
        }

        setFileUploads((prev) => prev.map((f) => (f.id === file.id ? { ...f, status: "success", progress: 100 } : f)));
      } catch (error: any) {
        console.error("[Upload] Registration failed:", error);
        // Handle registration error
        const errorMessage = error.message || "Failed to register file";
        setFileUploads((prev) =>
          prev.map((f) => (f.id === file.id ? { ...f, status: "error", error: errorMessage } : f))
        );
      }
    };

    // Upload error
    const handleError = (file: any, error: any) => {
      console.error("[Upload] Upload failed:", file.name, error);
      setFileUploads((prev) =>
        prev.map((f) =>
          f.id === file.id ? { ...f, status: "error", error: error.message || t("uploadFile.errors.uploadFailed") } : f
        )
      );
    };

    // All uploads complete
    const handleComplete = () => {
      setIsUploading(false);
    };

    uppy.on("file-added", handleFileAdded);
    uppy.on("upload-progress", handleProgress);
    uppy.on("upload-success", handleSuccess);
    uppy.on("upload-error", handleError);
    uppy.on("complete", handleComplete);

    return () => {
      uppy.off("file-added", handleFileAdded);
      uppy.off("upload-progress", handleProgress);
      uppy.off("upload-success", handleSuccess);
      uppy.off("upload-error", handleError);
      uppy.off("complete", handleComplete);
    };
  }, [t]); // Empty dependency array - callbacks use refs

  /**
   * Add files to upload queue
   */
  const addFiles = useCallback((files: File[]) => {
    const uppy = uppyRef.current;
    if (!uppy) return;

    files.forEach((file) => {
      try {
        uppy.addFile({
          name: file.name,
          type: file.type,
          data: file,
          meta: {
            relativePath: (file as any).webkitRelativePath || null,
          },
        });
      } catch (error: any) {
        console.error("[Upload] Error adding file:", error);
        toast.error(`Failed to add ${file.name}: ${error.message}`);
      }
    });
  }, []);

  /**
   * Start uploading all pending files
   */
  const startUpload = useCallback(() => {
    const uppy = uppyRef.current;
    if (!uppy) return;

    setIsUploading(true);
    uppy.upload();
  }, []);

  /**
   * Cancel a specific file upload
   */
  const cancelUpload = useCallback((fileId: string) => {
    const uppy = uppyRef.current;
    if (!uppy) return;

    uppy.removeFile(fileId);
    setFileUploads((prev) => prev.map((f) => (f.id === fileId ? { ...f, status: "cancelled" } : f)));
  }, []);

  /**
   * Retry a failed upload
   */
  const retryUpload = useCallback(
    (fileId: string) => {
      const uppy = uppyRef.current;
      if (!uppy) return;

      const file = fileUploads.find((f) => f.id === fileId);
      if (!file) return;

      // Reset status to pending
      setFileUploads((prev) => prev.map((f) => (f.id === fileId ? { ...f, status: "pending", error: undefined } : f)));

      // Retry the upload
      uppy.retryUpload(fileId);
    },
    [fileUploads]
  );

  /**
   * Remove a file from the upload queue
   */
  const removeFile = useCallback(
    (fileId: string) => {
      const uppy = uppyRef.current;
      if (!uppy) return;

      // Revoke preview URL if exists
      const file = fileUploads.find((f) => f.id === fileId);
      if (file?.previewUrl) {
        URL.revokeObjectURL(file.previewUrl);
      }

      uppy.removeFile(fileId);
      setFileUploads((prev) => prev.filter((f) => f.id !== fileId));
    },
    [fileUploads]
  );

  /**
   * Clear all files from the upload queue
   */
  const clearAll = useCallback(() => {
    const uppy = uppyRef.current;
    if (!uppy) return;

    // Revoke all preview URLs from current state
    setFileUploads((prev) => {
      prev.forEach((file) => {
        if (file.previewUrl) {
          URL.revokeObjectURL(file.previewUrl);
        }
      });
      return [];
    });

    // Remove all files from Uppy
    const uppyFiles = uppy.getFiles();
    uppyFiles.forEach((file) => {
      uppy.removeFile(file.id);
    });

    // Reset uploading state
    setIsUploading(false);
  }, []);

  return {
    addFiles,
    startUpload,
    cancelUpload,
    retryUpload,
    removeFile,
    clearAll,
    fileUploads,
    isUploading,
    uppy: uppyRef.current,
  };
}
